﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CC98.Network.Primitives;
using CC98.Security;

namespace CC98.Network
{
    public class CC98HttpClient : HttpClient
    {
        internal CC98HttpClient(CC98HttpClientHandler handler)
            : base(handler)
        {
            if (handler == null)
                throw new ArgumentNullException("handler");
            this.Handler = handler;
            this.Handler.AuthenticationStateChanged += HttpHandleAuthenticationStateChanged;
        }

        public bool IsAuthenticated
        {
            get
            {
                return this.Handler.IsAuthenticated;
            }
        }

        public int UserID
        {
            get
            {
                return this.Handler.UserID;
            }
        }

        public string Username
        {
            get
            {
                return this.Handler.Username;
            }
        }

        public DateTime? LoginTime
        {
            get
            {
                return this.Handler.LoginTime;
            }
        }

        public virtual async Task<bool> LoginAsync(CC98Certificate certificate)
        {
            if (certificate == null)
                throw new ArgumentNullException("certificate");
            if (certificate.IsAnonymous)
                throw new InvalidOperationException("Anonymous certificate cannot login");
            if (certificate.Username != this.Username)
                throw new ArgumentException("Username not match");
            if (this.IsAuthenticated)
                return true;
            // Login
            HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Post, LoginUrl);
            // Encoding
            FormUrlEncodedContent content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]
            {
                new KeyValuePair<string,string>("a", "i"),
                new KeyValuePair<string,string>("u", certificate.Username),
                new KeyValuePair<string, string>("p", this.GetEncodedPassword(certificate.Password)),
                new KeyValuePair<string,string>("userhidden","2")
            });
            reqMsg.Content = content;

            var resp = await this.SendAsync(reqMsg);
            var respContent = await resp.Content.ReadAsStringAsync();

            return respContent == "9898";
        }

        /// <summary>
        /// Get the session object represents the current client
        /// </summary>
        /// <returns></returns>
        public Session GetSession()
        {
            return this.Handler.GetSession();
        }

        /// <summary>
        /// Sine logout don't need any communication with server, so this is not an async method
        /// </summary>
        public virtual void Logout()
        {
            this.Handler.ResetHandler();
        }

        public virtual async Task<HttpResponseMessage> GetHomeAsync()
        {
            return await this.GetAsync(HomeUrl);
        }

        public virtual async Task<HttpResponseMessage> GetBoardAsync(int id, int pageNo = 1)
        {
            return await this.GetAsync(string.Format(BoardUrlFormat, id, pageNo));
        }

        public virtual async Task<HttpResponseMessage> GetThreadAsync(int boardID, int threadID, int pageNo = 1)
        {
            return await this.GetAsync(string.Format(ThreadUrlFormat, boardID, threadID, pageNo));
        }

        protected virtual void OnAuthenticationStateChanged()
        {
            if (this.AuthenticationStateChanged != null)
                this.AuthenticationStateChanged(this, new EventArgs());
        }

        public event EventHandler AuthenticationStateChanged;

        protected CC98HttpClientHandler Handler { get; private set; }

        protected void HttpHandleAuthenticationStateChanged(object sender, AuthenticationStateChangedEventArgs e)
        {
            this.OnAuthenticationStateChanged();
        }

        private string GetEncodedPassword(string password)
        {
            return password.ComputeMD5();
        }

        #region Url patterns

        /// <summary>
        /// Home url, no parameters
        /// </summary>
        private static readonly string HomeUrl = "http://www.cc98.org/";
        /// <summary>
        /// Board url. Parameter {0} is board id
        /// </summary>
        private static readonly string BoardUrlFormat = "http://www.cc98.org/list.asp?boardid={0}&page={1}";
        /// <summary>
        /// Thread url. Parameter {0} is board id, {1} is thread id, {2} is page number (start from 1)
        /// </summary>
        private static readonly string ThreadUrlFormat = "http://www.cc98.org/dispbbs.asp?boardID={0}&ID={1}&page={2}";
        /// <summary>
        /// Login url
        /// </summary>
        private static readonly string LoginUrl = "http://www.cc98.org/sign.asp";
        /// <summary>
        /// Post thread action url. Parameter {0} is board id, {1} is thread id
        /// </summary>
        private static readonly string PostThreadUrlFormat = "http://www.cc98.org/reannounce.asp?BoardID={0}&id={1}";

        #endregion
    }
}
